package crypter

import (
	"crypto/aes"
	"crypto/cipher"
	"encoding/base64"
	"encoding/hex"
	"errors"

	"gitee.com/gousing/helper/internal"
)

const (
	AesErrDataEmpty  = "AES data invalid: cannot be empty"
	AesErrDataDecode = "AES data invalid: can't decode"
	AesErrKey        = "AES key invalid: must to be 16 or 24 or 32 bytes long"
	AesErrIV         = "AES iv invalid: must be 16 bytes long"
)

// AesEncryptCBC 字节数据加密 (AES/CBC/PKCS7Padding)
//   - data 待加密的数据
//   - key 秘钥, 不同秘钥长度对应不同的加密位数: AES 128=> 16位Key / 192=> 24位Key / 256=> 32位Key
//   - iv 向量，AES Block Size固定为16位，超过16位时截取前16位
//   - 返回加密后的字节数组
func AesEncryptCBC(data []byte, key []byte, iv []byte) (encData []byte, err error) {
	if err = aesValidData(&data); err != nil {
		return
	}
	if err = aesValidKey(&key); err != nil {
		return
	}
	if err = aesValidIV(&iv); err != nil {
		return
	}
	// 分组秘钥
	var block cipher.Block
	block, err = aes.NewCipher(key)
	if err != nil {
		err = errors.New(AesErrKey)
		return
	}
	// 获取秘钥块的长度
	blockSize := block.BlockSize()
	// 加密模式
	blockMode := cipher.NewCBCEncrypter(block, iv)
	// 填充补全码
	paddingData := PKCS7Padding(data, blockSize)
	// 创建数组
	encData = make([]byte, len(paddingData))
	// 加密
	blockMode.CryptBlocks(encData, paddingData)
	return encData, nil
}

// AesEncryptCBCHex 字符串数据加密 (AES/CBC/PKCS7Padding)
//   - data 待加密的字符串
//   - key 秘钥, 不同秘钥长度对应不同的加密位数: AES 128=> 16位Key / 192=> 24位Key / 256=> 32位Key
//   - iv 向量, AES Block Size固定为16位，超过16位时截取前16位
//   - 返回 Hex 16进制格式的加密结果(小写)
func AesEncryptCBCHex(data string, key string, iv string) (string, error) {
	dataBytes, err := AesEncryptCBC(internal.StringToBytes(data), internal.StringToBytes(key), internal.StringToBytes(iv))
	if err != nil {
		return "", err
	}
	return hex.EncodeToString(dataBytes), nil
}

// AesEncryptCBCBase64 字符串数据加密 (AES/CBC/PKCS7Padding)
//   - data 待加密的字符串
//   - key 秘钥, 不同秘钥长度对应不同的加密位数: AES 128=> 16位Key / 192=> 24位Key / 256=> 32位Key
//   - iv 向量, AES Block Size固定为16位，超过16位时截取前16位
//   - 返回 Base64格式的加密结果
func AesEncryptCBCBase64(data string, key string, iv string) (string, error) {
	dataBytes, err := AesEncryptCBC(internal.StringToBytes(data), internal.StringToBytes(key), internal.StringToBytes(iv))
	if err != nil {
		return "", err
	}
	return base64.StdEncoding.EncodeToString(dataBytes), nil
}

// AesDecryptCBC 字节数组数据解密 (运算模式: CBC, 填充模式: PKCS7)
//   - decData 待解密的字节数组数据
//   - key 秘钥, 不同秘钥长度对应不同的加密位数: AES 128=> 16位Key / 192=> 24位Key / 256=> 32位Key
//   - iv 向量, AES Block Size固定为16位，超过16位时截取前16位
//   - 返回解密后的字节数组
func AesDecryptCBC(data []byte, key []byte, iv []byte) (decData []byte, err error) {
	if err = aesValidData(&data); err != nil {
		return
	}
	if err = aesValidKey(&key); err != nil {
		return
	}
	if err = aesValidIV(&iv); err != nil {
		return
	}
	// 分组秘钥
	var block cipher.Block
	block, err = aes.NewCipher(key)
	if err != nil {
		//AES Key 字符长度必须为 AES 128=> 16 / 192=> 24 / 256=> 32长度
		err = errors.New(AesErrKey)
		return
	}
	// 获取秘钥块的长度(AES Block Size固定为16位)
	blockSize := block.BlockSize()
	// 解密模式
	blockMode := cipher.NewCBCDecrypter(block, iv)
	// 解密数据
	decData = make([]byte, len(data))
	// 解密
	blockMode.CryptBlocks(decData, data)
	// 移除补全码
	decData = PKCS7UnPadding(decData, blockSize)
	return decData, nil
}

// AesDecryptCBCHex  解密字符串数据 (AES/CBC/PKCS7Padding)
//   - data 待解密的字符串(Hex 16进制格式的加密结果(小写))
//   - key 秘钥, 不同秘钥长度对应不同的加密位数: AES 128=> 16位Key / 192=> 24位Key / 256=> 32位Key
//   - iv 向量, AES Block Size固定为16位，超过16位时截取前16位
//   - 返回解密后的字符串
func AesDecryptCBCHex(data string, key string, iv string) (string, error) {
	if len(data) < 1 {
		return "", errors.New(AesErrDataEmpty)
	}
	dataBytes, err := hex.DecodeString(data)
	if err != nil {
		return "", errors.New(AesErrDataEmpty)
	}
	decBytes, err := AesDecryptCBC(dataBytes, internal.StringToBytes(key), internal.StringToBytes(iv))
	if err != nil {
		return "", err
	}
	return internal.BytesToString(decBytes), nil
}

// AesDecryptCBCBase64   解密字符串数据 (AES/CBC/PKCS7Padding)
//   - data 待解密的字符串(Base64格式)
//   - key 秘钥, 不同秘钥长度对应不同的加密位数: AES 128=> 16位Key / 192=> 24位Key / 256=> 32位Key
//   - iv 向量, AES Block Size固定为16位，超过16位时截取前16位
//   - 返回解密后的字符串
func AesDecryptCBCBase64(data string, key string, iv string) (string, error) {
	if len(data) < 1 {
		return "", errors.New(AesErrDataEmpty)
	}
	dataBytes, err := base64.StdEncoding.DecodeString(data)
	if err != nil {
		return "", errors.New(AesErrDataEmpty)
	}
	decBytes, err := AesDecryptCBC(dataBytes, internal.StringToBytes(key), internal.StringToBytes(iv))
	if err != nil {
		return "", err
	}
	return internal.BytesToString(decBytes), nil
}

// AesEncryptCFB 数据加密 (AES/CFB/NoPadding)
//   - data 待加密的数据
//   - key 秘钥, 不同秘钥长度对应不同的加密位数: AES 128=> 16位Key / 192=> 24位Key / 256=> 32位Key
//   - iv 向量, AES Block Size固定为16位，超过16位时截取前16位
//   - 返回加密后的数据
func AesEncryptCFB(data []byte, key []byte, iv []byte) (encData []byte, err error) {
	if err = aesValidData(&data); err != nil {
		return
	}
	if err = aesValidKey(&key); err != nil {
		return
	}
	if err = aesValidIV(&iv); err != nil {
		return
	}
	// 分组秘钥
	var block cipher.Block
	block, err = aes.NewCipher(key)
	if err != nil {
		err = errors.New(AesErrKey)
		return
	}
	// 加密
	encrypter := cipher.NewCFBEncrypter(block, iv)
	encData = make([]byte, len(data))
	encrypter.XORKeyStream(encData, data)
	return encData, nil
}

// AesEncryptCFBHex 字符串数据加密 (AES/CFB/NoPadding)
//   - data 待加密的字符串
//   - key 秘钥, 不同秘钥长度对应不同的加密位数: AES 128=> 16位Key / 192=> 24位Key / 256=> 32位Key
//   - iv 向量, AES Block Size固定为16位，超过16位时截取前16位
//   - 返回 Hex 16进制格式的加密结果(小写)
func AesEncryptCFBHex(data string, key string, iv string) (string, error) {
	encData, err := AesEncryptCFB(internal.StringToBytes(data), internal.StringToBytes(key), internal.StringToBytes(iv))
	if err != nil {
		return "", err
	}
	return hex.EncodeToString(encData), nil
}

// AesEncryptCFBBase64 字符串数据加密  (AES/CFB/NoPadding)
//   - data 待加密的字符串
//   - key 秘钥, 不同秘钥长度对应不同的加密位数: AES 128=> 16位Key / 192=> 24位Key / 256=> 32位Key
//   - iv 向量, AES Block Size固定为16位，超过16位时截取前16位
//   - 返回 Base64格式的加密结果
func AesEncryptCFBBase64(data string, key string, iv string) (string, error) {
	encData, err := AesEncryptCFB(internal.StringToBytes(data), internal.StringToBytes(key), internal.StringToBytes(iv))
	if err != nil {
		return "", err
	}
	return base64.StdEncoding.EncodeToString(encData), nil
}

// AesDecryptCFB 数据解密 (AES/CFB/NoPadding)
//   - data 待解密的数据
//   - key 秘钥, 不同秘钥长度对应不同的加密位数: AES 128=> 16位Key / 192=> 24位Key / 256=> 32位Key
//   - iv 向量, AES Block Size固定为16位，超过16位时截取前16位
//   - 返回解密后的数据
func AesDecryptCFB(data []byte, key []byte, iv []byte) (decData []byte, err error) {
	if err = aesValidData(&data); err != nil {
		return
	}
	if err = aesValidKey(&key); err != nil {
		return
	}
	if err = aesValidIV(&iv); err != nil {
		return
	}
	// 分组秘钥
	var block cipher.Block
	block, err = aes.NewCipher(key)
	if err != nil {
		err = errors.New(AesErrKey)
		return
	}
	// 解密
	decrypter := cipher.NewCFBDecrypter(block, iv)
	decData = make([]byte, len(data))
	decrypter.XORKeyStream(decData, data)
	return decData, nil
}

// AesDecryptCFBHex   解密字符串数据 (AES/CFB/NoPadding)
//   - data 待解密的字符串(Hex 16进制格式的加密结果(小写))
//   - key 秘钥, 不同秘钥长度对应不同的加密位数: AES 128=> 16位Key / 192=> 24位Key / 256=> 32位Key
//   - iv 向量, AES Block Size固定为16位，超过16位时截取前16位
//   - 返回解密后的字符串
func AesDecryptCFBHex(data string, key string, iv string) (string, error) {
	if len(data) < 1 {
		return "", errors.New(AesErrDataEmpty)
	}
	dataBytes, err := hex.DecodeString(data)
	if err != nil {
		return "", errors.New(AesErrDataDecode)
	}
	decBytes, err := AesDecryptCFB(dataBytes, internal.StringToBytes(key), internal.StringToBytes(iv))
	if err != nil {
		return "", err
	}
	return internal.BytesToString(decBytes), err
}

// AesDecryptCFBBase64  解密字符串数据(AES/CFB/NoPadding)
//   - data 待解密的字符串(Base64格式)
//   - key 秘钥, 不同秘钥长度对应不同的加密位数: AES 128=> 16位Key / 192=> 24位Key / 256=> 32位Key
//   - iv 向量, AES Block Size固定为16位，超过16位时截取前16位
//   - 返回解密后的字符串
func AesDecryptCFBBase64(data string, key string, iv string) (string, error) {
	if len(data) < 1 {
		return "", errors.New(AesErrDataEmpty)
	}
	dataBytes, err := base64.StdEncoding.DecodeString(data)
	if err != nil {
		return "", errors.New(AesErrDataDecode)
	}
	decBytes, err := AesDecryptCFB(dataBytes, internal.StringToBytes(key), internal.StringToBytes(iv))
	if err != nil {
		return "", err
	}
	return internal.BytesToString(decBytes), err
}
func aesValidData(data *[]byte) error {
	if len(*data) < 1 {
		return errors.New(AesErrDataEmpty)
	}
	return nil
}
func aesValidKey(key *[]byte) error {
	switch len(*key) {
	// 不同秘钥长度对应不同的加密位数: AES 128=> 16位Key / 192=> 24位Key / 256=> 32位Key
	case 32, 24, 16:
		return nil
	default:
		return errors.New(AesErrKey)
	}
}
func aesValidIV(iv *[]byte) error {
	if len(*iv) < 16 {
		return errors.New(AesErrKey)
	} else if len(*iv) > 16 {
		*iv = (*iv)[:16]
	}
	return nil
}
